import { Camera } from "@babylonjs/core/Cameras/camera";
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder";
import { TransformNode } from "@babylonjs/core/Meshes/transformNode";
import { Scene } from "@babylonjs/core/scene";
import { CanvasTextureText } from "../../texture/canvas-texture-text";
import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial";
import { Vector3 } from "@babylonjs/core/Maths/math.vector";
import { BabylonContainer } from '../../base/babylon-container';
import { WebXRCamera } from "@babylonjs/core/XR/webXRCamera";


/**
 * 3d调试输出参数对象
 * @param position 位置
 * @param offset 位置偏移
 * @param size 尺寸大小
 * @param messageCount 同时显示的最大信息数量
 * @param renderGroupID 渲染顺序值 
 * @param fontColor 文字颜色
 * @param rowLength 单行内容最大长度
 * @param forward 前方偏移值（锁定到相机时）
 * @param scattered 是否始终面向相机
 * @param lockToCamera 是否锁定到相机
 */
interface IDebugOptions {
    position?: Vector3,
    offset?: Vector3,
    size?: number,
    messageCount?: number,
    renderGroupID?: number,
    fontColor?: string,
    rowLength?: number,
    forward?: number,
    scattered?: boolean,
    lockToCamera?: boolean,
}


/**
 * 3d调试输出（仅开发中使用，使用initialize方法初始化后再使用）；
 */
class Debug {

    private static _container: TransformNode;
    private static _root: TransformNode;
    private static _count: number;
    private static _msgs: AbstractMesh[] = [];
    private static _enabled = true;
    private static _isVisible = true;
    private static _index = 0;
    private static _curW = 0;
    private static _curH = 0;
    private static _renderGrp: number = 1;
    private static _fontColor: string;
    private static _rowLength: number;
    private static _position = new Vector3();
    private static _s = 0;
    private static _forward: number;
    private static _tv0 = new Vector3();
    private static _cameraSwitchHandle: { (): void };
    private static _camera: Camera | null = null;
    private static _offset = new Vector3();
    private static _scattered: boolean;
    private static _lockToCamera: boolean;

    public static scene: Scene | undefined;

    /**
     * 初始化
     * @param scene 所在场景
     * @param options 参数对象
     */
    public static initialize(scene: Scene, options?: IDebugOptions) {
        this.scene = scene;
        const ops = options || {};


        this._root = new TransformNode('vr-debug-root', scene);
        this._container = new TransformNode('vr-debug-container', scene);
        this._container.parent = this._root;
        this.position = ops.position || new Vector3(0, 0, 0);
        this._container.billboardMode = this._scattered ? TransformNode.BILLBOARDMODE_NONE : TransformNode.BILLBOARDMODE_ALL;
        this.offset = ops.offset || new Vector3(0, 0, 0);
        this.size = ops.size || 4;
        this._count = ops.messageCount || 10;
        this._fontColor = ops.fontColor || '#ff00ff';
        this._rowLength = ops.rowLength || 40;
        this._forward = ops.forward || 10;
        this._scattered = ops.scattered === undefined ? true : ops.scattered;
        this.lockToCamera = ops.lockToCamera || false;

        this._cameraSwitchHandle = (): void => {
            this.onCameraSwitch();
        };
        this.scene.onActiveCameraChanged.add(this._cameraSwitchHandle);

    }

    /**
     * 输出信息（如含有类、函数等，不能保证正常输出）
     * @param msg 要输出的信息
     */
    public static log(...msg: any[]): void {
        if (!this._enabled) return;
        try {
            let str = this._index + '>';
            for (const ms of msg) {
                str += ' ' + JSON.stringify(ms);
            }
            str = str.replace(/\"/g, '');

            this.createMessage(str);
            this.updatePosition();
            this._index++;

            while (this._msgs.length > this._count) {
                const index = 0;
                this._msgs[index].dispose(true, true);
                this._msgs.splice(index, 1);
            }

        } catch (err) {
            console.warn(err);
        }

    }

    /**
     * 设置总体大小
     */
    public static set size(size: number) {
        this._s = size;
        this._container.scaling.set(this._s, this._s, this._s);
    }

    /**
     * 设置位置偏移
     */
    public static set offset(offset: Vector3) {
        this._container.position = this._offset.copyFrom(offset);
    }

    /**
     * 设置位置
     */
    public static set position(position: Vector3) {
        this._root.position = this._position.copyFrom(position);
    }

    /**
     * 设置是否可见
     */
    public static set isVisible(bol: boolean) {
        this._isVisible = bol;
        for (const msg of this._msgs) {
            msg.isVisible = bol;
        }
    }

    /**
     * 获取是否可见
     */
    public static get isVisible(): boolean {
        return this._isVisible;
    }

    /**
     * 设置是否可用
     */
    public static set enabled(bol: boolean) {
        this._enabled = bol;
    }

    /**
     * 获取是否可用
     */
    public static get enabled(): boolean {
        return this._enabled;
    }

    /**
     * 设置是否锁定到相机
     */
    public static set lockToCamera(isLock: boolean) {
        this.attachToCamera(isLock, true);
    }

    /**
     * 定位到相机
     */
    public static toCamera(): void {
        if (!this.scene) return;
        const cam = this.scene.activeCamera;
        if (!cam) return;
        const root = this._root;
        const pos = cam.globalPosition;
        const dir = cam.getDirection(Vector3.Forward());
        root.position = this._tv0.copyFrom(pos).addInPlace(dir.scale(BabylonContainer.isRightHand ? -this._forward : this._forward));
    }

    /**
     * 挂载到相机
     * @param isLock 是否锁定到相机
     * @param record 是否记录当前锁定状态
     */
    private static attachToCamera(isLock: boolean, record: boolean): void {
        if (record) this._lockToCamera = isLock;
        if (!this.scene) return;
        const cam = this.scene.activeCamera;
        if (!cam) return;
        if (isLock) {
            this._container.billboardMode = TransformNode.BILLBOARDMODE_NONE;
            this._camera = cam;
            this._root.parent = cam instanceof WebXRCamera ? cam.rigCameras[0] : cam;
            this._root.position.set(0, 0, BabylonContainer.isRightHand ? -this._forward : this._forward);
        } else {
            this._root.parent = null;
            this._root.position = this._position;
            this._container.billboardMode = this._scattered ? TransformNode.BILLBOARDMODE_NONE : TransformNode.BILLBOARDMODE_ALL;
            this._camera = null;
        }
    }

    /**
     * 更新位置
     */
    private static updatePosition(): void {
        for (let i = 0; i < this._msgs.length - 1; i++) {
            const msg = this._msgs[i];
            msg.position.y += this._curH;
        }
    }

    /**
     * 创建单条输出信息
     * @param str 要输出的内容
     */
    private static createMessage(str: string): void {
        const ctt = new CanvasTextureText('vr-debug-message-texture-' + this._index, {
            text: str,
            vivid: 2,
            fontColor: this._fontColor,
            rowLength: this._rowLength,
        }, this.scene);

        const msg = this.createLabelPlane(ctt.width, ctt.height);
        const material = msg.material as StandardMaterial;
        material.disableLighting = true;
        material.emissiveTexture = ctt.texture;
        material.opacityTexture = ctt.texture;
        this._msgs.push(msg);
    }

    /**
     * 创建标签平面（用于显示内容）
     * @param width 宽度
     * @param height 高度
     * @returns 网格
     */
    private static createLabelPlane(width: number, height: number): AbstractMesh {
        this._curW = width;
        this._curH = height;
        const plane = MeshBuilder.CreatePlane('vr-debug-message-mesh' + this._index, { size: 1 }, this.scene);
        plane.scaling.set(this._curW, this._curH, 1);
        plane.position.set(0, 0, 0);
        const material = new StandardMaterial('vr-debug-message-mesh-material' + this._index, this.scene);
        plane.material = material;
        plane.parent = this._container;
        plane.renderingGroupId = this._renderGrp;
        plane.isPickable = false;
        plane.billboardMode = this._scattered ? TransformNode.BILLBOARDMODE_ALL : TransformNode.BILLBOARDMODE_NONE;
        return plane;
    }

    /**
     * 切换活动相机时的响应函数
     */
    private static onCameraSwitch(): void {
        if (!this.scene) return;
        this.attachToCamera(false, false);
        this._camera = this.scene.activeCamera;
        if (!this._camera) return;
        this.attachToCamera(this._lockToCamera, false);
    }

}


export { Debug };
export type { IDebugOptions };